﻿#region Copyright
// Copyright (c) 2011-2012 RaisingForce Team (Alikin Sergey)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using RaisingForce.Raise.Language.Model.Rules;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using RaisingForce.Raise.Language.Model;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Xml.Serialization;

namespace RaisingForce.Raise.Tests
{


    /// <summary>
    ///Это класс теста для RuleSetTest, в котором должны
    ///находиться все модульные тесты RuleSetTest
    ///</summary>
    [TestClass()]
    public class RuleSetTest
    {


        private TestContext testContextInstance;

        /// <summary>
        ///Получает или устанавливает контекст теста, в котором предоставляются
        ///сведения о текущем тестовом запуске и обеспечивается его функциональность.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Дополнительные атрибуты теста
        // 
        //При написании тестов можно использовать следующие дополнительные атрибуты:
        //
        //ClassInitialize используется для выполнения кода до запуска первого теста в классе
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //ClassCleanup используется для выполнения кода после завершения работы всех тестов в классе
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //TestInitialize используется для выполнения кода перед запуском каждого теста
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //TestCleanup используется для выполнения кода после завершения каждого теста
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion

        /// <summary>
        ///Тест для CreateFromData
        ///</summary>
        [TestMethod()]
        public void CreateFromDataTest()
        {
            IWordFabric wordFabric = new WordFabric();
            // отсутствие наборов правил
            RuleSetCollection ruleSetCollection = new RuleSetCollection();
            this.testRuleSetCollection(ruleSetCollection, wordFabric);
            // один набор правил с одним правилом
            RuleSet ruleSet1 =
                new RuleSet("RuleSet1")
                {
                    Rules = new RuleCollection
                    {
                        new Rule(
                            wordFabric.CreateWord<IfWord>(
                                WordTypeNames.ComeFromChain, "B"),
                            wordFabric.CreateWord<ThenWord>(
                                WordTypeNames.MoveTwice, "Forward", "Left"),
                            wordFabric.CreateChainWord("A"))
                    }
                };
            ruleSetCollection.Add(ruleSet1);
            Assert.AreEqual(1, ruleSetCollection.Count);
            Assert.IsTrue(ruleSetCollection.Contains(ruleSet1));
            this.testRuleSetCollection(ruleSetCollection, wordFabric);
            // один набор правил с двумя правилами
            ruleSet1.Rules.Add(
                new Rule(
                    wordFabric.CreateWord<IfWord>(WordTypeNames.ObjectNear),
                    wordFabric.CreateWord<ThenWord>(WordTypeNames.IncrementCounter),
                    wordFabric.CreateChainWord("B")));
            this.testRuleSetCollection(ruleSetCollection, wordFabric);
            // один набор правил с тремя правилами
            ruleSet1.Rules.Add(
                new Rule(
                    wordFabric.CreateWord<IfWord>(
                        WordTypeNames.ObjectThere,
                        secondaryGlyphValue: "Backward"),
                    wordFabric.CreateWord<ThenWord>(
                        WordTypeNames.BypassObject,
                        secondaryGlyphValue: "Right"),
                    wordFabric.CreateChainWord("B")));
            this.testRuleSetCollection(ruleSetCollection, wordFabric);
            // один набор правил с тремя правилами и один без правил
            RuleSet ruleSet2 = ruleSetCollection.CreateRuleSet();
            Assert.AreEqual(2, ruleSetCollection.Count);
            Assert.AreEqual("RuleSet2", ruleSet2.Name);
            Assert.IsTrue(ruleSetCollection.Contains(ruleSet2));
            this.testRuleSetCollection(ruleSetCollection, wordFabric);
            // один набор правил с тремя правилами и один с двумя правилами
            ruleSet2.Rules.Add(ruleSet1.Rules[1]);
            ruleSet2.Rules.Add(ruleSet1.Rules[2]);
            this.testRuleSetCollection(ruleSetCollection, wordFabric);
            // два набора правил, оба без правил
            ruleSet1.Rules.Clear();
            ruleSet2.Rules.Clear();
            this.testRuleSetCollection(ruleSetCollection, wordFabric);
            // один набор правил без правил
            ruleSetCollection.Remove(ruleSet2.Name);
            Assert.AreEqual(1, ruleSetCollection.Count);
            Assert.IsFalse(ruleSetCollection.Contains(ruleSet2));
            this.testRuleSetCollection(ruleSetCollection, wordFabric);
        }

        private void testRuleSetCollection(RuleSetCollection expected, IWordFabric wordFabric)
        {
            Assert.IsNotNull(expected);
            Assert.IsNotNull(wordFabric);
            RuleSetDataCollection expectedData = this.getDataWithAssertions(expected);
            RuleSetCollection actual;
            actual = RuleSetCollection.CreateFromData(expectedData, wordFabric);
            this.assertRuleSetCollectionsAreEqual(actual, expectedData);
            RuleSetDataCollection actualData = this.getDataWithAssertions(actual);
            this.assertRuleSetCollectionsAreEqual(expected, actualData);
        }

        private RuleSetDataCollection getDataWithAssertions(RuleSetCollection ruleSetCollection)
        {
            Assert.IsNotNull(ruleSetCollection);
            RuleSetDataCollection ruleSetCollectionData = ruleSetCollection.GetData();
            this.assertRuleSetCollectionsAreEqual(ruleSetCollection, ruleSetCollectionData);
            RuleSetDataCollection ruleSetDataSerialized = this.getDataAfterSerialization(ruleSetCollectionData);
            this.assertRuleSetCollectionsAreEqual(ruleSetCollection, ruleSetDataSerialized);
            RuleSetDataCollection ruleSetDataXmlSerialized = this.getDataAfterXmlSerialization(ruleSetCollectionData);
            this.assertRuleSetCollectionsAreEqual(ruleSetCollection, ruleSetDataXmlSerialized);
            return ruleSetCollectionData;
        }

        private RuleSetDataCollection getDataAfterSerialization(RuleSetDataCollection data)
        {
            Assert.IsNotNull(data);
            SerializationHelper.Test(ref data);
            return data;
        }

        private RuleSetDataCollection getDataAfterXmlSerialization(RuleSetDataCollection data)
        {
            Assert.IsNotNull(data);
            SerializationHelper.Test(ref data);
            return data;
        }

        private void assertRuleSetCollectionsAreEqual(
            RuleSetCollection expected,
            RuleSetDataCollection actual)
        {
            Assert.IsNotNull(expected);
            Assert.IsNotNull(actual);
            Assert.AreEqual(expected.Count, actual.Count);
            for (int i = 0; i < expected.Count; i++)
            {
                this.assertRuleSetsAreEqual(expected[i], actual[i]);
            }
        }

        private void assertRuleSetsAreEqual(RuleSet expected, RuleSetData actual)
        {
            Assert.IsNotNull(expected);
            Assert.IsNotNull(actual);
            Assert.AreEqual(expected.Name, actual.Name);
            Assert.AreEqual(expected.Rules.Count, actual.Rules.Count);
            for (int i = 0; i < expected.Rules.Count; i++)
            {
                this.assertRulesAreEqual(expected.Rules[i], actual.Rules[i]);
            }
        }

        private void assertRulesAreEqual(Rule expected, RuleData actual)
        {
            Assert.IsNotNull(expected);
            Assert.IsNotNull(actual);
            this.assertWordsAreEqual(expected.IfWord, actual.IfWordData);
            this.assertWordsAreEqual(expected.ThenWord, actual.ThenWordData);
            this.assertWordsAreEqual(expected.ChainWord, actual.ChainWordData);
        }

        private void assertWordsAreEqual(Word expected, WordData actual)
        {
            Assert.IsNotNull(expected);
            Assert.IsNotNull(actual);
            Assert.AreEqual(expected.WordType.Name, actual.WordTypeName);
            Assert.AreEqual(expected.PrimaryGlyph.Value, actual.PrimaryGlyphValue);
            Assert.IsTrue(expected.SecondaryGlyph == null && actual.SecondaryGlyphValue == null
                || expected.SecondaryGlyph.Value == actual.SecondaryGlyphValue);
        }
    }
}
